<!doctype html><html lang=en-us dir=ltr><head><meta charset=UTF-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="网络分层图 # ![[net_layer.png]]
TCP是传输层的协议，而基于TCP造出来的HTTP和各类RPC协议，它们都只是定义了不同消息格式的应用层协议而已。 HTTP协议（Hyper Text Transfer Protocol），又叫做超文本传输协议。我们用的比较多，平时上网在浏览器上敲个网址就能访问网页，这里用到的就是HTTP协议。 RPC（Remote Procedure Call），又叫做远程过程调用。它本身并不是一个具体的协议，而是一种调用方式。虽然大部分RPC协议底层使用TCP，但实际上它们不一定非得使用TCP，改用UDP或者HTTP，其实也可以做到类似的功能。 RPC # vs HTTP # HTTP: ![[http.png]] RPC: ![[rpc.png]]
服务发现 [x] # 首先要向某个服务器发起请求，你得先建立连接，而建立连接的前提是，你得知道IP地址和端口。这个找到服务对应的IP端口的过程，其实就是服务发现。
在HTTP中，你知道服务的域名，就可以通过DNS服务去解析得到它背后的IP地址，默认80端口。
而RPC的话，就有些区别，一般会有专门的中间服务去保存服务名和IP信息，比如consul或者etcd，甚至是redis。想要访问某个服务，就去这些中间服务去获得IP和端口信息。由于dns也是服务发现的一种，所以也有基于dns去做服务发现的组件，比如CoreDNS。
底层连接形式 [x] # 以主流的HTTP1.1协议为例，其默认在建立底层TCP连接之后会一直保持这个连接（keep alive），之后的请求和响应都会复用这条连接。
而RPC协议，也跟HTTP类似，也是通过建立TCP长链接进行数据交互，但不同的地方在于，RPC协议一般还会再建个连接池，在请求量大的时候，建立多条连接放在池内，要发数据的时候就从池里取一条连接出来，用完放回去，下次再复用，可以说非常环保。
由于连接池有利于提升网络请求性能，所以不少编程语言的网络库里都会给HTTP加个连接池，比如go就是这么干的。
可以看出这一块两者也没太大区别，所以也不是关键。
传输的内容 # 基于TCP传输的消息，说到底，无非都是消息头header和消息体body。
header是用于标记一些特殊信息，其中最重要的是消息体长度。
body则是放我们真正需要传输的内容，而这些内容只能是二进制01串，毕竟计算机只认识这玩意。所以TCP传字符串和数字都问题不大，因为字符串可以转成编码再变成01串，而数字本身也能直接转为二进制。但结构体呢，我们得想个办法将它也转为二进制01串，这样的方案现在也有很多现成的，比如json，protobuf。
这个将结构体转为二进制数组的过程就叫序列化，反过来将二进制数组复原成结构体的过程叫反序列化。 ![[tobytes.png]]
序列化和反序列化
对于主流的HTTP1.1，虽然它现在叫超文本协议，支持音频视频，但HTTP设计初是用于做网页文本展示的，所以它传的内容以字符串为主。header和body都是如此。在body这块，它使用json来序列化结构体数据。
我们可以随便截个图直观看下。 ![[tobetes_details.png]] HTTP报文
可以看到这里面的内容非常多的冗余，显得非常啰嗦。最明显的，像header里的那些信息，其实如果我们约定好头部的第几位是content-type，就不需要每次都真的把&#34;content-type&#34;这个字段都传过来，类似的情况其实在body的json结构里也特别明显。
而RPC，因为它定制化程度更高，可以采用体积更小的protobuf或其他序列化协议去保存结构体数据，同时也不需要像HTTP那样考虑各种浏览器行为，比如302重定向跳转啥的。因此性能也会更好一些，这也是在公司内部微服务中抛弃HTTP，选择使用RPC的最主要原因。
当然上面说的HTTP，其实特指的是现在主流使用的HTTP1.1，HTTP2在前者的基础上做了很多改进，所以性能可能比很多RPC协议还要好，甚至连gRPC底层都直接用的HTTP2。
结论 # 纯裸TCP是能收发数据，但它是个无边界的数据流，上层需要定义消息格式用于定义消息边界。于是就有了各种协议，HTTP和各类RPC协议就是在TCP之上定义的应用层协议。 RPC本质上不算是协议，而是一种调用方式，而像gRPC和thrift这样的具体实现，才是协议，它们是实现了RPC调用的协议。目的是希望程序员能像调用本地方法那样去调用远端的服务方法。同时RPC有很多种实现方式，不一定非得基于TCP协议。 从发展历史来说，**HTTP主要用于b/s架构，而RPC更多用于c/s架构。但现在其实已经没分那么清了，b/s和c/s在慢慢融合。**很多软件同时支持多端，所以对外一般用HTTP协议，而内部集群的微服务之间则采用RPC协议进行通讯。 RPC其实比HTTP出现的要早，且比目前主流的HTTP1.1性能要更好，所以大部分公司内部都还在使用RPC。 HTTP2.0在HTTP1.1的基础上做了优化，性能可能比很多RPC协议都要好，但由于是这几年才出来的，所以也不太可能取代掉RPC。 TCP # 八股文常背，TCP是有三个特点，面向连接、可靠、基于字节流。
粘包问题 # 因为： ![[tcp_is_dataflow.png]] 所以： ![[nianbaowenti.png]] 无法区分上述两种情况。 得出： 纯裸TCP是不能直接拿来用的，你需要在这个基础上加入一些自定义的规则，用于区分消息边界。
于是我们会把每条要发送的数据都包装一下，比如加入消息头，消息头里写清楚一个完整的包长度是多少，根据这个长度可以继续接收数据，截取出来后它们就是我们真正要传输的消息体。"><meta name=theme-color content="#FFFFFF"><meta name=color-scheme content="light dark"><meta property="og:title" content="网络编程"><meta property="og:description" content="网络分层图 # ![[net_layer.png]]
TCP是传输层的协议，而基于TCP造出来的HTTP和各类RPC协议，它们都只是定义了不同消息格式的应用层协议而已。 HTTP协议（Hyper Text Transfer Protocol），又叫做超文本传输协议。我们用的比较多，平时上网在浏览器上敲个网址就能访问网页，这里用到的就是HTTP协议。 RPC（Remote Procedure Call），又叫做远程过程调用。它本身并不是一个具体的协议，而是一种调用方式。虽然大部分RPC协议底层使用TCP，但实际上它们不一定非得使用TCP，改用UDP或者HTTP，其实也可以做到类似的功能。 RPC # vs HTTP # HTTP: ![[http.png]] RPC: ![[rpc.png]]
服务发现 [x] # 首先要向某个服务器发起请求，你得先建立连接，而建立连接的前提是，你得知道IP地址和端口。这个找到服务对应的IP端口的过程，其实就是服务发现。
在HTTP中，你知道服务的域名，就可以通过DNS服务去解析得到它背后的IP地址，默认80端口。
而RPC的话，就有些区别，一般会有专门的中间服务去保存服务名和IP信息，比如consul或者etcd，甚至是redis。想要访问某个服务，就去这些中间服务去获得IP和端口信息。由于dns也是服务发现的一种，所以也有基于dns去做服务发现的组件，比如CoreDNS。
底层连接形式 [x] # 以主流的HTTP1.1协议为例，其默认在建立底层TCP连接之后会一直保持这个连接（keep alive），之后的请求和响应都会复用这条连接。
而RPC协议，也跟HTTP类似，也是通过建立TCP长链接进行数据交互，但不同的地方在于，RPC协议一般还会再建个连接池，在请求量大的时候，建立多条连接放在池内，要发数据的时候就从池里取一条连接出来，用完放回去，下次再复用，可以说非常环保。
由于连接池有利于提升网络请求性能，所以不少编程语言的网络库里都会给HTTP加个连接池，比如go就是这么干的。
可以看出这一块两者也没太大区别，所以也不是关键。
传输的内容 # 基于TCP传输的消息，说到底，无非都是消息头header和消息体body。
header是用于标记一些特殊信息，其中最重要的是消息体长度。
body则是放我们真正需要传输的内容，而这些内容只能是二进制01串，毕竟计算机只认识这玩意。所以TCP传字符串和数字都问题不大，因为字符串可以转成编码再变成01串，而数字本身也能直接转为二进制。但结构体呢，我们得想个办法将它也转为二进制01串，这样的方案现在也有很多现成的，比如json，protobuf。
这个将结构体转为二进制数组的过程就叫序列化，反过来将二进制数组复原成结构体的过程叫反序列化。 ![[tobytes.png]]
序列化和反序列化
对于主流的HTTP1.1，虽然它现在叫超文本协议，支持音频视频，但HTTP设计初是用于做网页文本展示的，所以它传的内容以字符串为主。header和body都是如此。在body这块，它使用json来序列化结构体数据。
我们可以随便截个图直观看下。 ![[tobetes_details.png]] HTTP报文
可以看到这里面的内容非常多的冗余，显得非常啰嗦。最明显的，像header里的那些信息，其实如果我们约定好头部的第几位是content-type，就不需要每次都真的把&#34;content-type&#34;这个字段都传过来，类似的情况其实在body的json结构里也特别明显。
而RPC，因为它定制化程度更高，可以采用体积更小的protobuf或其他序列化协议去保存结构体数据，同时也不需要像HTTP那样考虑各种浏览器行为，比如302重定向跳转啥的。因此性能也会更好一些，这也是在公司内部微服务中抛弃HTTP，选择使用RPC的最主要原因。
当然上面说的HTTP，其实特指的是现在主流使用的HTTP1.1，HTTP2在前者的基础上做了很多改进，所以性能可能比很多RPC协议还要好，甚至连gRPC底层都直接用的HTTP2。
结论 # 纯裸TCP是能收发数据，但它是个无边界的数据流，上层需要定义消息格式用于定义消息边界。于是就有了各种协议，HTTP和各类RPC协议就是在TCP之上定义的应用层协议。 RPC本质上不算是协议，而是一种调用方式，而像gRPC和thrift这样的具体实现，才是协议，它们是实现了RPC调用的协议。目的是希望程序员能像调用本地方法那样去调用远端的服务方法。同时RPC有很多种实现方式，不一定非得基于TCP协议。 从发展历史来说，**HTTP主要用于b/s架构，而RPC更多用于c/s架构。但现在其实已经没分那么清了，b/s和c/s在慢慢融合。**很多软件同时支持多端，所以对外一般用HTTP协议，而内部集群的微服务之间则采用RPC协议进行通讯。 RPC其实比HTTP出现的要早，且比目前主流的HTTP1.1性能要更好，所以大部分公司内部都还在使用RPC。 HTTP2.0在HTTP1.1的基础上做了优化，性能可能比很多RPC协议都要好，但由于是这几年才出来的，所以也不太可能取代掉RPC。 TCP # 八股文常背，TCP是有三个特点，面向连接、可靠、基于字节流。
粘包问题 # 因为： ![[tcp_is_dataflow.png]] 所以： ![[nianbaowenti.png]] 无法区分上述两种情况。 得出： 纯裸TCP是不能直接拿来用的，你需要在这个基础上加入一些自定义的规则，用于区分消息边界。
于是我们会把每条要发送的数据都包装一下，比如加入消息头，消息头里写清楚一个完整的包长度是多少，根据这个长度可以继续接收数据，截取出来后它们就是我们真正要传输的消息体。"><meta property="og:type" content="article"><meta property="og:url" content="http://example.org/docs/programmer/net/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"><meta property="article:section" content="docs"><meta property="article:published_time" content="2024-05-06T00:00:00+00:00"><meta property="article:modified_time" content="2024-05-06T00:00:00+00:00"><title>网络编程 | Ian's Blog</title>
<link rel=manifest href=/manifest.json><link rel=icon href=/favicon.png type=image/x-icon><link rel=stylesheet href=/book.min.c58292d36b18b675680ab9baea2029204537b839ea72f258746ec0f32ce8d6c8.css integrity="sha256-xYKS02sYtnVoCrm66iApIEU3uDnqcvJYdG7A8yzo1sg=" crossorigin=anonymous><script defer src=/flexsearch.min.js></script><script defer src=/en.search.min.80b0c12c6a3982c23bab5eb1d10c75b4cf8bfbaa1c7e183729648ce8a09207d7.js integrity="sha256-gLDBLGo5gsI7q16x0Qx1tM+L+6ocfhg3KWSM6KCSB9c=" crossorigin=anonymous></script></head><body dir=ltr><input type=checkbox class="hidden toggle" id=menu-control>
<input type=checkbox class="hidden toggle" id=toc-control><main class="container flex"><aside class=book-menu><div class=book-menu-content><nav><h2 class=book-brand><a class="flex align-center" href=/><span>Ian's Blog</span></a></h2><div class=book-search><input type=text id=book-search-input placeholder=Search aria-label=Search maxlength=64 data-hotkeys=s/><div class="book-search-spinner hidden"></div><ul id=book-search-results></ul></div><ul><li class=book-section-flat><a href=/docs/programmer/>程序员笔记</a><ul><li><input type=checkbox id=section-d5f99046a51e5e750b61f2e037945fcc class=toggle>
<label for=section-d5f99046a51e5e750b61f2e037945fcc class="flex justify-between"><a role=button>基础工具和配置</a></label><ul><li><a href=/docs/programmer/basetc/%E6%96%87%E6%9C%AC%E4%B8%89%E5%89%91%E5%AE%A2/>文本三剑客</a></li><li><a href=/docs/programmer/basetc/tmux/>Tmux使用笔记</a></li><li><a href=/docs/programmer/basetc/obsidian%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE%E6%8F%92%E4%BB%B6/>Obsidian思维导图插件</a></li><li><a href=/docs/programmer/basetc/for_china/>各个软件换源</a></li><li><a href=/docs/programmer/basetc/tipsofvim/>tip Of vim</a></li><li><a href=/docs/programmer/basetc/editer/>编辑器使用</a></li><li><a href=/docs/programmer/basetc/bash/>Bash</a></li><li><a href=/docs/programmer/basetc/gitbook/>Gitbook</a></li><li><a href=/docs/programmer/basetc/vim/>Vim</a></li><li><a href=/docs/programmer/basetc/%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B/>supervisor</a></li></ul></li><li><input type=checkbox id=section-8658298e10b544e890095f646916165a class=toggle>
<label for=section-8658298e10b544e890095f646916165a class="flex justify-between"><a href=/docs/programmer/cloudnative/>云原生</a></label><ul><li><input type=checkbox id=section-31d3b02fd4a132635e483e7e756058ea class=toggle>
<label for=section-31d3b02fd4a132635e483e7e756058ea class="flex justify-between"><a href=/docs/programmer/cloudnative/redis/>中间件</a></label><ul><li><a href=/docs/programmer/cloudnative/redis/python-redis-%E5%AE%A2%E6%88%B7%E7%AB%AF/>Python Redis 客户端</a></li><li><a href=/docs/programmer/cloudnative/redis/bigkey-and-hotkey/>大key、热key问题</a></li><li><a href=/docs/programmer/cloudnative/redis/the-basics-of-redis/>Redis基础</a></li><li><a href=/docs/programmer/cloudnative/redis/advanced-knowledge-of-redis/>Redis进阶</a></li><li><a href=/docs/programmer/cloudnative/redis/%E4%BD%BF%E7%94%A8%E5%91%BD%E4%BB%A4/>Redis常用命令</a></li></ul></li><li><input type=checkbox id=section-62d608ed890b3abc76dae78ccfcab912 class=toggle>
<label for=section-62d608ed890b3abc76dae78ccfcab912 class="flex justify-between"><a role=button>k8s</a></label><ul><li><a href=/docs/programmer/cloudnative/k8s/elk%E5%9C%A8k8s%E4%B8%8A%E7%9A%84%E9%83%A8%E7%BD%B2%E4%BD%BF%E7%94%A8%E7%A4%BA%E4%BE%8B/>elk在k8s上的部署使用示例</a></li><li><a href=/docs/programmer/cloudnative/k8s/k8s-%E9%85%8D%E5%A5%97%E8%AF%B4%E6%98%8E/>k8s 配套说明</a></li><li><a href=/docs/programmer/cloudnative/k8s/k8s-%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/>k8s技术分享</a></li><li><a href=/docs/programmer/cloudnative/k8s/k8s%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E5%92%8C%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%A7%A3%E6%9E%90/>k8s学习-常用命令和配置文件</a></li><li><a href=/docs/programmer/cloudnative/k8s/argo-workflow%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%92%8C%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E5%88%86%E6%9E%90/>Argo Workflow性能测试和使用场景分析</a></li><li><a href=/docs/programmer/cloudnative/k8s/argo-%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/>Argo 使用记录</a></li></ul></li><li><input type=checkbox id=section-b828bf3d116bc282da9db25a06bf908e class=toggle>
<label for=section-b828bf3d116bc282da9db25a06bf908e class="flex justify-between"><a role=button>中间件</a></label><ul><li><a href=/docs/programmer/cloudnative/middleware/kafka-%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8/>Kafka 安装和使用</a></li></ul></li><li><a href=/docs/programmer/cloudnative/%E5%AE%B9%E5%99%A8/note-of-podman/>Podman</a></li><li><a href=/docs/programmer/cloudnative/nginx%E5%AE%9E%E7%94%A8%E9%85%8D%E7%BD%AE/>Nginx实用配置</a></li><li><a href=/docs/programmer/cloudnative/uwsgi-%E5%A4%84%E7%90%86%E8%AE%B0%E5%BD%95/>uwsgi 处理记录</a></li><li><a href=/docs/programmer/cloudnative/note-of-docker/>Docker</a></li><li><a href=/docs/programmer/cloudnative/%E5%AE%B9%E5%99%A8/note-of-docker/>Docker</a></li><li><a href=/docs/programmer/cloudnative/fastapi/>Django的建站的(｡･･)ﾉﾞ</a></li><li><a href=/docs/programmer/cloudnative/nginx-%E9%AB%98%E5%8F%AF%E7%94%A8/>Nginx高可用</a></li><li><a href=/docs/programmer/cloudnative/notesdjango/>Django的建站的(｡･･)ﾉﾞ</a></li><li><a href=/docs/programmer/cloudnative/sonar-%E4%BB%A3%E7%A0%81%E9%9D%99%E6%80%81%E6%A3%80%E6%9F%A5/>Sonar 代码静态检查</a></li></ul></li><li><input type=checkbox id=section-883e27361d38e16afb68faff3435ac0b class=toggle>
<label for=section-883e27361d38e16afb68faff3435ac0b class="flex justify-between"><a role=button>机器学习</a></label><ul><li><a href=/docs/programmer/ml/stable-diffusion/>AI画图</a></li><li><a href=/docs/programmer/ml/%E7%88%AC%E8%99%AB/>爬虫</a></li><li><a href=/docs/programmer/ml/paddle/>Paddle</a></li><li><a href=/docs/programmer/ml/tensorflow/>Tensorflow</a></li><li><a href=/docs/programmer/ml/opencv/>OpenCV</a></li><li><a href=/docs/programmer/ml/yolo/>Demo Test项目中的一些东西</a></li><li><a href=/docs/programmer/ml/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%B8%B8%E7%94%A8%E5%BA%93%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95/>机器学习库</a></li></ul></li><li><input type=checkbox id=section-0bf4f4329214e20fa67ca3e12c6aad0c class=toggle>
<label for=section-0bf4f4329214e20fa67ca3e12c6aad0c class="flex justify-between"><a role=button>平台</a></label><ul><li><a href=/docs/programmer/platforms/ubuntu/>Ubuntu</a></li><li><a href=/docs/programmer/platforms/%E9%98%BF%E9%87%8C%E4%BA%91%E4%BD%BF%E7%94%A8/>阿里云使用</a></li><li><a href=/docs/programmer/platforms/wps-for-linux/>WPS for Linux</a></li><li><a href=/docs/programmer/platforms/appsflyer/>AppsFlyer-外网移动归因营销分析平台</a></li></ul></li><li><input type=checkbox id=section-bf4e0d6f0b81f7b3ec08ed1fc66b874d class=toggle>
<label for=section-bf4e0d6f0b81f7b3ec08ed1fc66b874d class="flex justify-between"><a href=/docs/programmer/langs/>编程语言</a></label><ul><li><input type=checkbox id=section-771df6c720301e69f1715f7fc174ac3d class=toggle>
<label for=section-771df6c720301e69f1715f7fc174ac3d class="flex justify-between"><a role=button>Python</a></label><ul><li><a href=/docs/programmer/langs/python/sqlalchemy/>SqlAlchemy - 数据库Orm</a></li><li><a href=/docs/programmer/langs/python/pypi/>PyPi使用说明</a></li><li><a href=/docs/programmer/langs/python/pytest/>PyTest</a></li><li><a href=/docs/programmer/langs/python/paramiko-%E4%BD%BF%E7%94%A8-sshsftp/>Paramiko 使用 Ssh&amp;sftp</a></li><li><a href=/docs/programmer/langs/python/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%A4%9A%E8%BF%9B%E7%A8%8B/>Python 多线程多进程</a></li><li><a href=/docs/programmer/langs/python/notespython/>Python 常用库</a></li><li><a href=/docs/programmer/langs/python/notespython/>Python 笔记</a></li><li><a href=/docs/programmer/langs/python/py%E5%B0%8F%E5%B7%A5%E5%85%B7%E5%92%8C%E5%8A%9F%E8%83%BD%E6%80%A7%E6%96%B9%E6%B3%95/>Py小工具和功能性方法</a></li><li><a href=/docs/programmer/langs/python/notespython/>解决问题</a></li></ul></li><li><input type=checkbox id=section-9f8ac8f06e138c7ac13ff61f23b4d497 class=toggle>
<label for=section-9f8ac8f06e138c7ac13ff61f23b4d497 class="flex justify-between"><a role=button>Golang</a></label><ul><li><a href=/docs/programmer/langs/golang/advanced-knowledge-of-golang/>Golang进阶笔记</a></li><li><a href=/docs/programmer/langs/golang/noteofgoexp/>Golang进阶笔记</a></li><li><a href=/docs/programmer/langs/golang/note-of-golang/>Golang笔记</a></li><li><a href=/docs/programmer/langs/golang/noteofgolang/>Golang笔记</a></li></ul></li><li><a href=/docs/programmer/langs/cmake/>CMake 使用Tips</a></li><li><a href=/docs/programmer/langs/tips-of-debugers/>Tips of debuggers</a></li><li><a href=/docs/programmer/langs/tips-of-markdown/>Tips of MarkDown</a></li><li><a href=/docs/programmer/langs/java/notesjava/>愉快的Java(happy to learn the fuck java)</a></li><li><a href=/docs/programmer/langs/note-for-fmtdata/>数据格式笔记</a></li></ul></li><li><input type=checkbox id=section-de7bfad1d124522974cdf8addfbb23f2 class=toggle checked>
<label for=section-de7bfad1d124522974cdf8addfbb23f2 class="flex justify-between"><a role=button>Net</a></label><ul><li><a href=/docs/programmer/net/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/ class=active>网络编程</a></li><li><a href=/docs/programmer/net/nginx%E5%AE%9E%E7%94%A8%E9%85%8D%E7%BD%AE/>Nginx实用配置</a></li><li><a href=/docs/programmer/net/tips-of-grpc/>gRpc使用小记</a></li><li><a href=/docs/programmer/net/epoll%E5%AE%9E%E7%8E%B0/>Epoll实现</a></li><li><a href=/docs/programmer/net/net/>Epoll实现</a></li></ul></li><li><input type=checkbox id=section-4446dd07527142b855f26d7cc8f0e617 class=toggle>
<label for=section-4446dd07527142b855f26d7cc8f0e617 class="flex justify-between"><a href=/docs/programmer/database/>Database</a></label><ul><li><a href=/docs/programmer/database/mongodb/>Mongodb使用记录</a></li><li><a href=/docs/programmer/database/dgraph/>Dgraph使用小记</a></li><li><a href=/docs/programmer/database/note-of-db-data-mongodb/>数据库-MongoDB篇</a></li><li><a href=/docs/programmer/database/note-of-db-data/>数据库-MongoDB篇</a></li><li><a href=/docs/programmer/database/note-of-db-data-mysql/>数据库-MySQL篇</a></li></ul></li><li><input type=checkbox id=section-7e5360c5e7954906b897ed79085884b6 class=toggle>
<label for=section-7e5360c5e7954906b897ed79085884b6 class="flex justify-between"><a href=/docs/programmer/gui/>图形用户界面-GUI</a></label><ul><li><a href=/docs/programmer/gui/pyinstaller-python%E6%89%93%E5%8C%85/>python打包</a></li><li><a href=/docs/programmer/gui/pyinstaller/>python打包</a></li><li><a href=/docs/programmer/gui/qt/>Qt/PySide</a></li><li><a href=/docs/programmer/gui/noteofvn_py/>Vn.Py学习笔记（Python交易平台框架）</a></li><li><a href=/docs/programmer/gui/vn.py%E7%AC%94%E8%AE%B0-%E4%BA%A4%E6%98%93%E5%B9%B3%E5%8F%B0%E5%AE%A2%E6%88%B7%E7%AB%AF/>Vn.Py学习笔记（Python交易平台框架）</a></li><li><a href=/docs/programmer/gui/notespython/>图形化界面 （Python Gui）</a></li><li><a href=/docs/programmer/gui/notespython/>图形化界面 （Python Gui）</a></li></ul></li><li><input type=checkbox id=section-ddcbe632dc99a9fb372422dada8ee641 class=toggle>
<label for=section-ddcbe632dc99a9fb372422dada8ee641 class="flex justify-between"><a role=button>OS操作系统问题处理</a></label><ul><li><a href=/docs/programmer/os/install_some/>安装问题</a></li><li><a href=/docs/programmer/os/android/>安卓</a></li><li><a href=/docs/programmer/os/freebsd/>FreeBSD</a></li><li><a href=/docs/programmer/os/note-of-linux/>Linux 笔记</a></li><li><a href=/docs/programmer/os/npm/>Npm</a></li><li><a href=/docs/programmer/os/git/>Git</a></li><li><a href=/docs/programmer/os/problem-of-windows/>Windows 爬坑记</a></li><li><a href=/docs/programmer/os/tips-of-problems/>解决问题记录笔记</a></li></ul></li><li><input type=checkbox id=section-d325c59fc6513e1b1e05a60b192d4973 class=toggle>
<label for=section-d325c59fc6513e1b1e05a60b192d4973 class="flex justify-between"><a href=/docs/programmer/hardware/>硬件</a></label><ul><li><a href=/docs/programmer/hardware/raspberrypi/>Raspberry Pi</a></li><li><a href=/docs/programmer/hardware/screen/>Screen</a></li></ul></li></ul></li><li class=book-section-flat><span>建模和游戏</span><ul><li><a href=/docs/3dgame/blender/>Blender</a></li><li><a href=/docs/3dgame/noteofue4/>UE4 笔记</a></li></ul></li><li class=book-section-flat><a href=/docs/example/>Hugo特殊Markdown语法说明</a><ul><li><a href=/docs/example/table-of-contents/>Table of Contents</a><ul><li><a href=/docs/example/table-of-contents/with-toc/>With ToC</a></li><li><a href=/docs/example/table-of-contents/without-toc/>Without ToC</a></li></ul></li><li><a href=/docs/example/shortcodes/buttons/>Buttons</a></li><li><a href=/docs/example/shortcodes/columns/>Columns</a></li><li><a href=/docs/example/shortcodes/details/>Details</a></li><li><a href=/docs/example/shortcodes/expand/>Expand</a></li><li><a href=/docs/example/shortcodes/hints/>Hints</a></li><li><a href=/docs/example/shortcodes/tabs/>Tabs</a></li></ul></li></ul><ul><li><a href=/posts/>Blog</a></li></ul></nav><script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script></div></aside><div class=book-page><header class=book-header><div class="flex align-center justify-between"><label for=menu-control><img src=/svg/menu.svg class=book-icon alt=Menu>
</label><strong>网络编程</strong>
<label for=toc-control><img src=/svg/toc.svg class=book-icon alt="Table of Contents"></label></div><aside class="hidden clearfix"><nav id=TableOfContents><ul><li><a href=#网络分层图>网络分层图</a></li><li><a href=#rpc>RPC</a><ul><li><a href=#vs-http>vs HTTP</a></li></ul></li><li><a href=#tcp>TCP</a><ul><li><a href=#粘包问题>粘包问题</a></li><li><a href=#建立连接示例>建立连接示例</a></li><li><a href=#三次握手>三次握手：</a></li><li><a href=#四次挥手>四次挥手：</a></li></ul></li><li><a href=#web问题>Web问题</a><ul><li><a href=#get-vs-post>GET vs POST</a></li><li><a href=#cookie和session>Cookie和Session</a></li><li><a href=#http和https>HTTP和HTTPS</a></li><li><a href=#csrf和xss>CSRF和XSS</a></li><li><a href=#cgi和wsgi>CGI和WSGI</a></li></ul></li></ul></nav></aside></header><article class=markdown><h2 id=网络分层图>网络分层图
<a class=anchor href=#%e7%bd%91%e7%bb%9c%e5%88%86%e5%b1%82%e5%9b%be>#</a></h2><p>![[net_layer.png]]</p><ul><li><strong>TCP是传输层的协议</strong>，而基于TCP造出来的HTTP和<strong>各类</strong>RPC协议，它们都只是定义了不同消息格式的<strong>应用层协议</strong>而已。</li><li><strong>HTTP</strong>协议（<strong>H</strong>yper <strong>T</strong>ext <strong>T</strong>ransfer <strong>P</strong>rotocol），又叫做<strong>超文本传输协议</strong>。我们用的比较多，平时上网在浏览器上敲个网址就能访问网页，这里用到的就是HTTP协议。</li><li><strong>RPC</strong>（<strong>R</strong>emote <strong>P</strong>rocedure <strong>C</strong>all），又叫做<strong>远程过程调用</strong>。它本身并不是一个具体的协议，而是一种<strong>调用方式</strong>。虽然大部分RPC协议底层使用TCP，但实际上<strong>它们不一定非得使用TCP，改用UDP或者HTTP，其实也可以做到类似的功能。</strong></li><li></li></ul><h2 id=rpc>RPC
<a class=anchor href=#rpc>#</a></h2><h3 id=vs-http>vs HTTP
<a class=anchor href=#vs-http>#</a></h3><p>HTTP:
![[http.png]]
RPC:
![[rpc.png]]</p><h4 id=服务发现-x>服务发现 [x]
<a class=anchor href=#%e6%9c%8d%e5%8a%a1%e5%8f%91%e7%8e%b0-x>#</a></h4><p>首先要向某个服务器发起请求，你得先建立连接，而建立连接的前提是，你得知道<strong>IP地址和端口</strong>。这个找到服务对应的IP端口的过程，其实就是<strong>服务发现</strong>。</p><p>在<strong>HTTP</strong>中，你知道服务的域名，就可以通过<strong>DNS服务</strong>去解析得到它背后的IP地址，默认80端口。</p><p>而<strong>RPC</strong>的话，就有些区别，一般会有专门的<strong>中间服务</strong>去保存服务名和IP信息，比如<strong>consul或者etcd，甚至是redis</strong>。想要访问某个服务，就去这些中间服务去获得IP和端口信息。由于dns也是服务发现的一种，所以也有基于dns去做服务发现的组件，比如<strong>CoreDNS</strong>。</p><h4 id=底层连接形式-x>底层连接形式 [x]
<a class=anchor href=#%e5%ba%95%e5%b1%82%e8%bf%9e%e6%8e%a5%e5%bd%a2%e5%bc%8f-x>#</a></h4><p>以主流的<strong>HTTP1.1</strong>协议为例，其默认在建立底层TCP连接之后会一直保持这个连接（<strong>keep alive</strong>），之后的请求和响应都会复用这条连接。</p><p>而<strong>RPC</strong>协议，也跟HTTP类似，也是通过建立TCP长链接进行数据交互，但不同的地方在于，RPC协议一般还会再建个<strong>连接池</strong>，在请求量大的时候，建立多条连接放在池内，要发数据的时候就从池里取一条连接出来，<strong>用完放回去，下次再复用</strong>，可以说非常环保。</p><p><strong>由于连接池有利于提升网络请求性能，所以不少编程语言的网络库里都会给HTTP加个连接池</strong>，比如<strong>go</strong>就是这么干的。</p><p>可以看出这一块两者也没太大区别，所以也不是关键。</p><h4 id=传输的内容>传输的内容
<a class=anchor href=#%e4%bc%a0%e8%be%93%e7%9a%84%e5%86%85%e5%ae%b9>#</a></h4><p>基于TCP传输的消息，说到底，无非都是<strong>消息头header和消息体body。</strong></p><p><strong>header</strong>是用于标记一些特殊信息，其中最重要的是<strong>消息体长度</strong>。</p><p><strong>body</strong>则是放我们真正需要传输的内容，而这些内容只能是二进制01串，毕竟计算机只认识这玩意。所以TCP传字符串和数字都问题不大，因为字符串可以转成编码再变成01串，而数字本身也能直接转为二进制。但结构体呢，我们得想个办法将它也转为二进制01串，这样的方案现在也有很多现成的，比如<strong>json，protobuf。</strong></p><p>这个将结构体转为二进制数组的过程就叫<strong>序列化</strong>，反过来将二进制数组复原成结构体的过程叫<strong>反序列化</strong>。
![[tobytes.png]]</p><p>序列化和反序列化</p><p>对于主流的HTTP1.1，虽然它现在叫<strong>超文本</strong>协议，支持音频视频，但HTTP设计初是用于做网页<strong>文本</strong>展示的，所以它传的内容以字符串为主。header和body都是如此。在body这块，它使用<strong>json</strong>来<strong>序列化</strong>结构体数据。</p><p>我们可以随便截个图直观看下。
![[tobetes_details.png]]
HTTP报文</p><p>可以看到这里面的内容非常多的<strong>冗余</strong>，显得<strong>非常啰嗦</strong>。最明显的，像<code>header</code>里的那些信息，其实如果我们约定好头部的第几位是content-type，就<strong>不需要每次都真的把"content-type"这个字段都传过来</strong>，类似的情况其实在<code>body</code>的json结构里也特别明显。</p><p>而RPC，因为它定制化程度更高，可以采用体积更小的protobuf或其他序列化协议去保存结构体数据，同时也不需要像HTTP那样考虑各种浏览器行为，比如302重定向跳转啥的。<strong>因此性能也会更好一些，这也是在公司内部微服务中抛弃HTTP，选择使用RPC的最主要原因。</strong></p><p>当然上面说的HTTP，其实<strong>特指的是现在主流使用的HTTP1.1</strong>，<code>HTTP2</code>在前者的基础上做了很多改进，所以<strong>性能可能比很多RPC协议还要好</strong>，甚至连<code>gRPC</code>底层都直接用的<code>HTTP2</code>。</p><h4 id=结论>结论
<a class=anchor href=#%e7%bb%93%e8%ae%ba>#</a></h4><ul><li>纯裸TCP是能收发数据，但它是个<strong>无边界</strong>的数据流，上层需要定义<strong>消息格式</strong>用于定义<strong>消息边界</strong>。于是就有了各种协议，HTTP和各类RPC协议就是在TCP之上定义的应用层协议。</li><li><strong>RPC本质上不算是协议，而是一种调用方式</strong>，而像gRPC和thrift这样的具体实现，才是协议，它们是实现了RPC调用的协议。目的是希望程序员能像调用本地方法那样去调用远端的服务方法。同时RPC有很多种实现方式，<strong>不一定非得基于TCP协议</strong>。</li><li>从发展历史来说，**HTTP主要用于b/s架构，而RPC更多用于c/s架构。但现在其实已经没分那么清了，b/s和c/s在慢慢融合。**很多软件同时支持多端，所以对外一般用HTTP协议，而内部集群的微服务之间则采用RPC协议进行通讯。</li><li>RPC其实比HTTP出现的要早，且比目前主流的HTTP1.1<strong>性能</strong>要更好，所以大部分公司内部都还在使用RPC。</li><li><strong>HTTP2.0</strong>在<strong>HTTP1.1</strong>的基础上做了优化，性能可能比很多RPC协议都要好，但由于是这几年才出来的，所以也不太可能取代掉RPC。</li></ul><h2 id=tcp>TCP
<a class=anchor href=#tcp>#</a></h2><blockquote><p>八股文常背，TCP是有三个特点，<strong>面向连接</strong>、<strong>可靠</strong>、基于<strong>字节流</strong>。</p></blockquote><h3 id=粘包问题>粘包问题
<a class=anchor href=#%e7%b2%98%e5%8c%85%e9%97%ae%e9%a2%98>#</a></h3><p>因为：
![[tcp_is_dataflow.png]]
所以：
![[nianbaowenti.png]]
无法区分上述两种情况。
得出：
纯裸TCP是不能直接拿来用的，你需要在这个基础上加入一些<strong>自定义的规则</strong>，用于区分<strong>消息边界</strong>。</p><p>于是我们会把每条要发送的数据都包装一下，比如加入<strong>消息头</strong>，<strong>消息头里写清楚一个完整的包长度是多少</strong>，根据这个长度可以继续接收数据，截取出来后它们就是我们真正要传输的<strong>消息体</strong>。</p><h3 id=建立连接示例>建立连接示例
<a class=anchor href=#%e5%bb%ba%e7%ab%8b%e8%bf%9e%e6%8e%a5%e7%a4%ba%e4%be%8b>#</a></h3><p>![[socket_connect.gif]]</p><h3 id=三次握手>三次握手：
<a class=anchor href=#%e4%b8%89%e6%ac%a1%e6%8f%a1%e6%89%8b>#</a></h3><ol><li>客户端通过向服务器端发送一个SYN来创建一个主动打开，作为三次握手的一部分。客户端把这段连接的序号设定为随机数 A。</li><li>服务器端应当为一个合法的SYN回送一个SYN/ACK。ACK 的确认码应为 A+1，SYN/ACK 包本身又有一个随机序号 B。</li><li>最后，客户端再发送一个ACK。当服务端受到这个ACK的时候，就完成了三路握手，并进入了连接创建状态。此时包序号被设定为收到的确认号 A+1，而响应则为 B+1。</li></ol><h4 id=如图3w4hpng>如图：![[3w4h.png]]
<a class=anchor href=#%e5%a6%82%e5%9b%be3w4hpng>#</a></h4><h3 id=四次挥手>四次挥手：
<a class=anchor href=#%e5%9b%9b%e6%ac%a1%e6%8c%a5%e6%89%8b>#</a></h3><p>注意: 中断连接端可以是客户端，也可以是服务器端。下面仅以客户端断开连接举例，反之亦然：</p><ol><li>客户端发送一个数据分段, 其中的 FIN 标记设置为1. 客户端进入 FIN-WAIT 状态. 该状态下客户端只接收数据, 不再发送数据.</li><li>服务器接收到带有 FIN = 1 的数据分段, 发送带有 ACK = 1 的剩余数据分段, 确认收到客户端发来的 FIN 信息.</li><li>服务器等到所有数据传输结束, 向客户端发送一个带有 FIN = 1 的数据分段, 并进入 CLOSE-WAIT 状态, 等待客户端发来带有 ACK = 1 的确认报文.</li><li>客户端收到服务器发来带有 FIN = 1 的报文, 返回 ACK = 1 的报文确认, 为了防止服务器端未收到需要重发, 进入 TIME-WAIT 状态. 服务器接收到报文后关闭连接. 客户端等待 2MSL 后未收到回复, 则认为服务器成功关闭, 客户端关闭连接.</li></ol><h2 id=web问题>Web问题
<a class=anchor href=#web%e9%97%ae%e9%a2%98>#</a></h2><h3 id=get-vs-post>GET vs POST
<a class=anchor href=#get-vs-post>#</a></h3><h4 id=有人想要的答案>有人想要的答案
<a class=anchor href=#%e6%9c%89%e4%ba%ba%e6%83%b3%e8%a6%81%e7%9a%84%e7%ad%94%e6%a1%88>#</a></h4><blockquote><p>但是这个不正确</p></blockquote><ol><li>GET使用URL或Cookie传参。而POST将数据放在BODY中。</li><li>GET的URL会有长度上的限制，则POST的数据则可以非常大。</li><li>POST比GET安全，因为数据在地址栏上不可见。</li></ol><h4 id=http协议对get和post都没有对长度的限制>HTTP协议对GET和POST都没有对长度的限制
<a class=anchor href=#http%e5%8d%8f%e8%ae%ae%e5%af%b9get%e5%92%8cpost%e9%83%bd%e6%b2%a1%e6%9c%89%e5%af%b9%e9%95%bf%e5%ba%a6%e7%9a%84%e9%99%90%e5%88%b6>#</a></h4><p>     HTTP协议明确地指出了，HTTP头和Body都没有长度的要求。而对于URL长度上的限制，有两方面的原因造成：</p><ol><li>浏览器。据说早期的浏览器会对URL长度做限制。据说IE对URL长度会限制在2048个字符内（流传很广，而且无数同事都表示认同）。但我自己试了一下，我构造了90K的URL通过IE9访问live.com，是正常的。网上的东西，哪怕是Wikipedia上的，也不能信。</li><li>服务器。URL长了，对服务器处理也是一种负担。原本一个会话就没有多少数据，现在如果有人恶意地构造几个几M大小的URL，并不停地访问你的服务器。服务器的最大并发数显然会下降。另一种攻击方式是，把告诉服务器Content-Length是一个很大的数，然后只给服务器发一点儿数据，嘿嘿，服务器你就傻等着去吧。哪怕你有超时设置，这种故意的次次访问超时也能让服务器吃不了兜着走。有鉴于此，多数服务器出于安全啦、稳定啦方面的考虑，会给URL长度加限制。但是这个限制是针对所有HTTP请求的，与GET、POST没有关系。</li></ol><h4 id=安全不安全和getpost没有关系>安全不安全和GET、POST没有关系
<a class=anchor href=#%e5%ae%89%e5%85%a8%e4%b8%8d%e5%ae%89%e5%85%a8%e5%92%8cgetpost%e6%b2%a1%e6%9c%89%e5%85%b3%e7%b3%bb>#</a></h4><h3 id=cookie和session>Cookie和Session
<a class=anchor href=#cookie%e5%92%8csession>#</a></h3><table><thead><tr><th style=text-align:left></th><th style=text-align:left>Cookie</th><th style=text-align:left>Session</th><th style=text-align:left></th></tr></thead><tbody><tr><td style=text-align:left>储存位置</td><td style=text-align:left>客户端</td><td style=text-align:left>服务器端</td><td style=text-align:left></td></tr><tr><td style=text-align:left>目的</td><td style=text-align:left>跟踪会话，也可以保存用户偏好设置或者保存用户名密码等</td><td style=text-align:left>跟踪会话</td><td style=text-align:left></td></tr><tr><td style=text-align:left>安全性</td><td style=text-align:left>不安全</td><td style=text-align:left>安全</td><td style=text-align:left></td></tr><tr><td style=text-align:left>session技术是要使用到cookie的，之所以出现session技术，主要是为了安全。</td><td></td><td></td><td></td></tr></tbody></table><h3 id=http和https>HTTP和HTTPS
<a class=anchor href=#http%e5%92%8chttps>#</a></h3><table><thead><tr><th style=text-align:left>状态码</th><th style=text-align:left>定义</th></tr></thead><tbody><tr><td style=text-align:left>1xx 报告</td><td style=text-align:left>接收到请求，继续进程</td></tr><tr><td style=text-align:left>2xx 成功</td><td style=text-align:left>步骤成功接收，被理解，并被接受</td></tr><tr><td style=text-align:left>3xx 重定向</td><td style=text-align:left>为了完成请求,必须采取进一步措施</td></tr><tr><td style=text-align:left>4xx 客户端出错</td><td style=text-align:left>请求包括错的顺序或不能完成</td></tr><tr><td style=text-align:left>5xx 服务器出错</td><td style=text-align:left>服务器无法完成显然有效的请求</td></tr><tr><td style=text-align:left>403: Forbidden 404: Not Found</td><td></td></tr><tr><td style=text-align:left>HTTPS握手,对称加密,非对称加密,TLS/SSL,RSA</td><td></td></tr></tbody></table><h3 id=csrf和xss>CSRF和XSS
<a class=anchor href=#csrf%e5%92%8cxss>#</a></h3><ul><li>CSRF(Cross-site request forgery)跨站请求伪造</li><li>XSS(Cross Site Scripting)跨站脚本攻击
CSRF 重点在请求，XSS 重点在脚本。</li></ul><h3 id=cgi和wsgi>CGI和WSGI
<a class=anchor href=#cgi%e5%92%8cwsgi>#</a></h3><p>CGI是通用网关接口，是连接web服务器和应用程序的接口，用户通过CGI来获取动态数据或文件等。 CGI程序是一个独立的程序，它可以用几乎所有语言来写，包括perl，c，lua，python等等。</p><p>WSGI, Web Server Gateway Interface，是Python应用程序或框架和Web服务器之间的一种接口，WSGI的其中一个目的就是让用户可以用统一的语言(Python)编写前后端。</p></article><footer class=book-footer><div class="flex flex-wrap justify-between"></div><script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){if(window.getSelection().toString())return;e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script></footer><div class=book-comments></div><label for=menu-control class="hidden book-menu-overlay"></label></div><aside class=book-toc><div class=book-toc-content><nav id=TableOfContents><ul><li><a href=#网络分层图>网络分层图</a></li><li><a href=#rpc>RPC</a><ul><li><a href=#vs-http>vs HTTP</a></li></ul></li><li><a href=#tcp>TCP</a><ul><li><a href=#粘包问题>粘包问题</a></li><li><a href=#建立连接示例>建立连接示例</a></li><li><a href=#三次握手>三次握手：</a></li><li><a href=#四次挥手>四次挥手：</a></li></ul></li><li><a href=#web问题>Web问题</a><ul><li><a href=#get-vs-post>GET vs POST</a></li><li><a href=#cookie和session>Cookie和Session</a></li><li><a href=#http和https>HTTP和HTTPS</a></li><li><a href=#csrf和xss>CSRF和XSS</a></li><li><a href=#cgi和wsgi>CGI和WSGI</a></li></ul></li></ul></nav></div></aside></main></body></html>